import argparse
import inspect
import json
import logging
import os
from typing import Any, Callable, Dict, List, Optional
from mcp.server.fastmcp import FastMCP
from pydantic import Field
from mcp_server.dwf_client import DWFClient
from mcp_server.dwf_api import DWFConfig, DWFAPIManager, DWFAPIDefinition

logging.basicConfig(
    level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)

logger = logging.getLogger(__name__)

# 定义类型别名
DWF_CLIENT_GETTER = Callable[[], DWFClient]
OVERRIDE_FUNC_TYPE = Callable[[FastMCP, DWF_CLIENT_GETTER, str], None]
VALIDATOR = Callable[[FastMCP, DWFClient, Dict[str, Any]], tuple[bool, str | None]]


class DWFMCPGenerator:
    """基于配置文件的DWF MCP生成器"""

    def __init__(self,
                 mcp: FastMCP,
                 api_config_file: str,
                 dwf_config: DWFConfig,
                 tool_configuration: Optional[Dict[str, Dict[str, Any]]] = None,
                 global_settings: Optional[Dict[str, Any]] = None):
        """
        初始化基于配置的DWF MCP生成器

        Args:
            mcp: FastMCP服务器实例
            api_config_file: API定义配置文件路径
            dwf_config: DWF实例配置
            tool_configuration: 工具配置字典
            global_settings: 全局设置
        """
        self.mcp = mcp
        self.api_config_file = api_config_file
        self.dwf_config = dwf_config
        self.tool_configuration = tool_configuration or {}
        self.global_settings = global_settings or {}
        self.dwf_client = DWFClient(dwf_config)

        self.api_manager = DWFAPIManager(api_config_file)
        self.api_definitions = list(self.api_manager.apis.values())
        self.__validate_tool_configuration()



    def generate(self):
        """根据配置动态生成MCP工具"""
        for api_def in self.api_definitions:
            api_name = api_def.name

            # 检查安全设置
            if self._is_operation_restricted(api_name):
                logger.warning(f"{api_name} is restricted by global settings. Skipping...")
                continue

            if api_name not in self.tool_configuration:
                # 没有特殊配置，使用默认生成
                func = self.__create_api_function(api_def)
                if func is not None:
                    self.mcp.tool(description=func.__doc__)(func)
            else:
                config = self.tool_configuration[api_name]

                # 检查是否忽略此API
                if config.get('ignore'):
                    logger.warning(f"Skipping API: {api_name} - {config.get('reason', 'set to ignore in config')}")
                    continue

                # 检查是否有函数覆盖
                if config.get('func_override') is not None:
                    fn = config.get('func_override')
                    self.__handle_function_override(api_name, fn)
                    continue

                # 使用配置生成函数
                func = self.__create_api_function(
                    api_def,
                    config.get('documentation_override'),
                    config.get('validator'),
                    config.get('parameter_overrides', {})
                )
                if func is not None:
                    self.mcp.tool(description=func.__doc__)(func)

    def _is_operation_restricted(self, api_name: str) -> bool:
        """检查操作是否受限"""
        security = self.global_settings.get('security', {})

        restricted_operations = security.get('restricted_operations', [])
        if any(op in api_name.lower() for op in restricted_operations):
            return True

        return False

    def __create_api_function(self,
                              api_def: DWFAPIDefinition,
                              documentation_override: Optional[str] = None,
                              validator: Optional[VALIDATOR] = None,
                              parameter_overrides: Optional[Dict[str, Any]] = None) -> Optional[Callable]:
        """为API定义创建对应的函数"""

        # 应用全局设置到参数覆盖
        global_defaults = {
            'language_type': self.global_settings.get('default_language', 'zh-CN'),
            'app_id': self.global_settings.get('default_app_id', 'default-app')
        }

        # 构建函数参数
        parameters = []
        type_mapping = {
            'str': str,
            'string': str,
            'int': int,
            'integer': int,
            'bool': bool,
            'boolean': bool,
            'dict': dict,
            'object': dict,
            'list': list,
            'array': list,
            'float': float,
            'number': float
        }

        for param in api_def.parameters:
            param_name = param["name"]
            param_type_str = param.get("type", "str")
            param_type = type_mapping.get(param_type_str, str)
            param_required = param.get("required", False)
            param_description = param.get("description", "")
            param_default = param.get("default")

            # 应用全局默认值
            if param_name in global_defaults and param_default is None:
                param_default = global_defaults[param_name]

            # 应用参数覆盖
            if parameter_overrides and param_name in parameter_overrides:
                override = parameter_overrides[param_name]
                param_type_str = override.get("type", param_type_str)
                param_type = type_mapping.get(param_type_str, str)
                param_required = override.get("required", param_required)
                param_description = override.get("description", param_description)
                param_default = override.get("default", param_default)

            if param_required:
                parameters.insert(0, inspect.Parameter(
                    name=param_name,
                    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
                    annotation=param_type
                ))
            else:
                from typing import Annotated
                default_value = param_default if param_default is not None else (
                    "" if param_type == str else
                    0 if param_type == int else
                    0.0 if param_type == float else
                    False if param_type == bool else
                    {} if param_type == dict else
                    []
                )

                parameters.append(inspect.Parameter(
                    name=param_name,
                    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
                    annotation=Annotated[
                        param_type,
                        Field(description=param_description)
                    ],
                    default=default_value
                ))

        # 创建异步函数
        async def api_function(*args, **kwargs) -> Dict[str, Any]:
            bound_args = api_function.__signature__.bind(*args, **kwargs)
            bound_args.apply_defaults()

            try:
                # 验证器检查
                if validator is not None:
                    status, msg = validator(self.mcp, self.dwf_client, bound_args.arguments)
                    if not status:
                        return {'error': msg}

                # 构建URL
                url_params = {}
                for param in api_def.parameters:
                    param_name = param["name"]
                    if "{" + param_name + "}" in api_def.url_template:
                        url_params[param_name] = bound_args.arguments[param_name]

                # 根据API类型获取正确的基础URL
                base_url = self.dwf_config.get_base_url(api_def.type)
                url = base_url + api_def.url_template.format(**url_params)

                # 构建headers
                headers = {}
                for header_name in api_def.headers:
                    if header_name in bound_args.arguments and bound_args.arguments[header_name]:
                        headers[header_name] = bound_args.arguments[header_name]

                # 构建查询参数
                params = {}
                for param_name in api_def.query_params:
                    if param_name in bound_args.arguments:
                        value = bound_args.arguments[param_name]
                        if isinstance(value, bool):
                            params[param_name] = str(value).lower()
                        elif value is not None and value != "" and value != [] and value != {}:
                            params[param_name] = value

                # 构建请求体数据
                data = None
                if api_def.method.upper() in ["POST", "PUT", "PATCH"]:
                    if api_def.body_params:
                        # 使用指定的body参数
                        if len(api_def.body_params) == 1:
                            body_param = api_def.body_params[0]
                            data = bound_args.arguments.get(body_param)
                        else:
                            data = {param: bound_args.arguments.get(param) for param in api_def.body_params}
                    else:
                        # 自动推断body参数（不在headers、query_params、url_template中的参数）
                        body_params = []
                        for param in api_def.parameters:
                            param_name = param["name"]
                            if (param_name not in api_def.headers and
                                    param_name not in api_def.query_params and
                                    "{" + param_name + "}" not in api_def.url_template):
                                body_params.append(param_name)

                        if len(body_params) == 1:
                            data = bound_args.arguments[body_params[0]]
                        elif len(body_params) > 1:
                            data = {param: bound_args.arguments[param] for param in body_params}

                # 发送请求
                timeout = self.global_settings.get('timeout_seconds', 30)
                response = self.dwf_client.request(
                    url=url,
                    method=api_def.method,
                    headers=headers if headers else None,
                    params=params if params else None,
                    data=data
                )

                return response

            except Exception as e:
                if self.global_settings.get('enable_debug', False):
                    import traceback
                    return {'error': str(e), 'traceback': traceback.format_exc()}
                return {'error': str(e)}

        # 设置函数元数据
        api_function.__name__ = api_def.name
        # 使用完整描述或覆盖描述
        api_function.__doc__ = documentation_override or api_def.get_full_description()
        api_function.__signature__ = inspect.Signature(parameters)

        return api_function

    def __handle_function_override(self, api_name: str, func_override: OVERRIDE_FUNC_TYPE) -> None:
        """处理函数覆盖"""

        def dwf_client_getter():
            return self.dwf_client

        func_override(self.mcp, dwf_client_getter, api_name)

    def __validate_tool_configuration(self):
        """验证工具配置"""
        for api_name, configuration in self.tool_configuration.items():
            if (configuration.get('ignore') is True and
                    configuration.get('func_override') is not None):
                raise ValueError(
                    f'For API {api_name}, cannot specify both ignore=True and a function override'
                )

            if (configuration.get('ignore') is True and
                    configuration.get('documentation_override') is not None and
                    configuration.get('documentation_override') != ''):
                raise ValueError(
                    f'For API {api_name}, cannot specify both ignore=True and a documentation override'
                )

            if (configuration.get('func_override') is not None and
                    configuration.get('documentation_override') is not None and
                    configuration.get('documentation_override') != ''):
                raise ValueError(
                    f'For API {api_name}, cannot specify both func_override and a documentation override'
                )


def create_config_based_dwf_mcp_server(dwf_config: DWFConfig,
                                       server_port: int = 9000,
                                       api_config_file: Optional[str] = None,
                                       tool_config_file: Optional[str] = None) -> FastMCP:
    """创建基于配置文件的DWF MCP服务器实例"""

    # 加载工具配置
    tool_configuration = {}
    global_settings = {}

    if tool_config_file and os.path.exists(tool_config_file):
        with open(tool_config_file, 'r', encoding='utf-8') as f:
            config = json.load(f)
            tool_configuration = config.get('tool_configuration', {})
            global_settings = config.get('global_settings', {})
            # 将security设置合并到global_settings中
            if 'security' in config:
                global_settings['security'] = config['security']

    # 创建MCP服务器
    mcp = FastMCP(
        'dwf-mcp-server',
        instructions="""DWF MCP Server - 提供DWF低代码平台的API访问能力，支持实体类、表单类、脚本类操作等功能。""",
        dependencies=['pydantic', 'requests'],
        port=server_port
    )

    # 创建生成器并生成工具
    generator = DWFMCPGenerator(
        mcp=mcp,
        api_config_file=api_config_file,
        dwf_config=dwf_config,
        tool_configuration=tool_configuration,
        global_settings=global_settings
    )
    generator.generate()

    return mcp


def main():
    """主函数，支持命令行参数"""
    parser = argparse.ArgumentParser(
        description='DWF MCP Server - 基于配置文件的DWF API桥接服务'
    )
    parser.add_argument(
        '--api-config',
        type=str,
        default='config/dwf_apis.json',
        help='API定义配置文件路径'
    )
    parser.add_argument(
        '--tool-config',
        default='config/tool_config_example.json',
        type=str,
        help='工具配置文件路径'
    )
    parser.add_argument(
        '--dwf-host',
        type=str,
        default='http://172.21.11.79',
        help='DWF服务器主机地址'
    )
    parser.add_argument(
        '--dwf-app-port',
        type=int,
        default=9090,
        help='DWF App端口'
    )
    parser.add_argument(
        '--dwf-modeler-port',
        type=int,
        default=6060,
        help='DWF Modeler端口'
    )
    parser.add_argument(
        '--dwf-username',
        type=str,
        default='admin',
        help='DWF用户名'
    )
    parser.add_argument(
        '--dwf-password',
        type=str,
        default='123456',
        help='DWF密码'
    )

    args = parser.parse_args()

    # 创建DWF配置
    dwf_config = DWFConfig(
        host=args.dwf_host,
        app_port=args.dwf_app_port,
        modeler_port=args.dwf_modeler_port,
        username=args.dwf_username,
        password=args.dwf_password
    )

    # 创建并运行服务器
    mcp = create_config_based_dwf_mcp_server(dwf_config, server_port=12008 ,api_config_file=args.api_config, tool_config_file=args.tool_config)
    mcp.run(transport='sse')

if __name__ == '__main__':
    main()
